package io.github.source.parse;

import android.text.TextUtils;

import com.github.catvod.net.OkHttp;
import com.github.catvod.utils.Json;
import com.google.common.net.HttpHeaders;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.orhanobut.logger.Logger;

import org.json.JSONObject;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

import io.github.source.App;
import io.github.source.CustomWebView;
import io.github.source.bean.Parse;
import io.github.source.config.VodConfig;
import io.github.source.media.bean.MediaPlay;
import io.github.source.util.UrlUtil;
import okhttp3.Headers;

/**
 * @ClassName: ParseJob
 * @Description: TODO(ParseJob) 解析获取的播放信息
 * @author: huangrongcai
 * @date: 2024/7/18
 * @Copyright: 黄嵘才（HuangRc)
 */
public class ParseJob {
    private static final String TAG = "Source.Media.Play.Provider.ParseJob";
    private final List<CustomWebView> webViews;
    ;
    private ExecutorService executor;
    private ExecutorService infinite;
    private ParseCallback callback;
    private Parse parse;

    private MediaPlay play;

    public ParseJob() {
        this.executor = Executors.newFixedThreadPool(2);
        this.infinite = Executors.newCachedThreadPool();
        this.webViews = new ArrayList<>();
        this.callback = getCallback(null);
    }

    /**
     * 是不是需要解析播放信息
     *
     * @param play play
     * @return true 是需要解析播放信息； false 其他
     */
    public static boolean shouldParseMediaPlay(MediaPlay play) {
        if (play == null) return false;
        if (play.getParse(1) == 1 || play.getJx() == 1) {
            return true;
        }
        return false;
    }

    public static boolean supportedParse(MediaPlay play) {
        if (!VodConfig.hasParse()) {
            return false;
        }
        if (VodConfig.containFlag(play.getFlag())) {
            return true;
        }
        if (play.getJx() == 1) {
            return true;
        }
        return false;
    }

    /**
     * 设置解析的配置信息
     *
     * @param play     MediaPlay
     * @param useParse useParse
     */
    private void setParse(MediaPlay play, boolean useParse) {
        if (useParse) {
            parse = VodConfig.get().getParse();
        }
        if (play.getPlayUrl().startsWith("json:")) {
            parse = Parse.get(1, play.getPlayUrl().substring(5));
        }

        if (play.getPlayUrl().startsWith("parse:")) {
            parse = VodConfig.get().getParse(play.getPlayUrl().substring(6));
        }
        if (parse == null || parse.isEmpty()) {
            parse = Parse.get(0, play.getPlayUrl());
        }
        parse.setHeader(play.getHeader());
        parse.setClick(getClick(play));
    }

    private String getClick(MediaPlay result) {
        String click = VodConfig.get().getSite(result.getKey()).getClick();
        if (!TextUtils.isEmpty(click)) return click;
        return result.getClick();
    }

    private void onParseError() {
        Logger.t(TAG).e("解析失败");
    }

    private Runnable getTask(MediaPlay result) {
        return () -> {
            try {
                doInBackground("", "", "");
            } catch (Throwable e) {
                throw new RuntimeException(e);
            }
        };
    }

    public static ParseJob create(MediaPlay play) {
        Logger.t(TAG).d(play);
        ParseJob job = new ParseJob();
        job.play = play;

        return job;
    }


    public void start() {
        start(getCallback(null));
    }

    public MediaPlay execute() {
        if (shouldParseMediaPlay(play)) {
            stopParse();
            try {
                setParse(play, true);
                return parse(play.getKey(), play.getUrl().v(), play.getFlag());
            } catch (Throwable e) {
                Logger.t(TAG).e(e, "parse 解析异常");
            }
        }

        return play;
    }

    public void enqueue() {
        executor.execute(() -> {
            try {
                setParse(play, true);
                executor.submit(getTask(play)).get(15 * 1000, TimeUnit.MILLISECONDS);
            } catch (Throwable e) {
                onParseError();
            }
        });
    }

    public void start(ParseCallback callback) {
        this.callback = callback;
        enqueue();

    }

    private void stopParse() {
        //停止之前的解析
//        if (executor != null) executor.shutdownNow();
//        if (infinite != null) infinite.shutdownNow();
//        infinite = null;
//        executor = null;
//        callback = null;
//        stopWeb();
    }

    private void doInBackground(String key, String webUrl, String flag) throws Throwable {
        parse(key, webUrl, flag);

    }

    private MediaPlay parse(String key, String webUrl, String flag) throws Throwable {
        Parse parse = getParse();
        Logger.t(TAG).d("--> 解析播放信息 " + parse);
        switch (parse.getType()) {
            case 0: //嗅探
                startWeb(key, parse, webUrl);
                break;
            case 1: //Json
                play = jsonParse(parse, webUrl, true);
                break;
            case 2: //Json擴展
                jsonExtend(webUrl);
                break;
            case 3: //Json聚合
                jsonMix(webUrl, flag);
                break;
            case 4: //超級解析
                godParse(webUrl, flag);
                break;
        }

        return checkParseMediaPlay();
    }

    private Parse getParse() {
        return parse;
    }

    /**
     * 解析Json
     *
     * @param item   Parse
     * @param webUrl String
     * @param error  error
     * @throws Exception
     */
    private MediaPlay jsonParse(Parse item, String webUrl, boolean error) throws Exception {
        String body = OkHttp.newCall(item.getUrl() + webUrl, Headers.of(item.getHeaders())).execute().body().string();
        JsonObject object = Json.parse(body).getAsJsonObject();
        Logger.t(TAG).d(body);
        //解析获取
        return checkResult(getHeader(object), getUrl(object), item.getName(), error);
    }

    private String getUrl(JsonObject object) {
        String url = Json.safeString(object, "url");
        if (url.isEmpty()) {
            JsonObject data = object.getAsJsonObject("data");
            url = Json.safeString(data, "url");
        }
        return url;
    }

    private Map<String, String> getHeader(JsonObject object) {
        Map<String, String> headers = new HashMap<>();
        for (Map.Entry<String, JsonElement> entry : object.entrySet()) {
            if (entry.getKey().equalsIgnoreCase(HttpHeaders.USER_AGENT) || entry.getKey().equalsIgnoreCase(HttpHeaders.REFERER)) {
                headers.put(UrlUtil.fixHeader(entry.getKey()), object.get(entry.getKey()).getAsString());
            }
        }
        if (headers.isEmpty()) return parse.getHeaders();
        return headers;
    }

    private MediaPlay checkResult(Map<String, String> headers, String url, String from, boolean error) {
        if (isPass(headers, url)) {
            onParseSuccess(headers, url, from);
            return createPlay(headers, url, from);
        } else if (error) {
            onParseError();
            play.setParse(0);
            return play;
        }
        return play;
    }

    private void checkResult(MediaPlay play) {
        play.setHeader(parse.getExt().getHeader());

        if (play.getUrl().isEmpty()) {/*没有获取播放地址*/
            onParseError();
        } else if (play.getParse() == 1) {/*继续解析*/
            startWeb(play.getHeaders(), UrlUtil.convert(play.getUrl().v()));
        } else {
            createPlay(play.getHeaders(), play.getUrl().v(), play.getJxFrom());
        }
    }

    private void onParseSuccess(Map<String, String> headers, String url, String from) {

    }

    private MediaPlay createPlay(Map<String, String> headers, String url, String from) {
        if (play != null) {
            play.setHeader(headers);
            play.setPlayUrl(url);
            play.setJxFrom(from);
        }
        return play;
    }

    //尝试这个地址是可以访问
    private boolean isPass(Map<String, String> headers, String url) {
        try {
            if (url.length() < 40) return false;
            return OkHttp.newCall(url, Headers.of(headers)).execute().code() == 200;
        } catch (Exception e) {
            return false;
        }
    }

    private void jsonExtend(String webUrl) throws Throwable {
        LinkedHashMap<String, String> jxs = new LinkedHashMap<>();
        for (Parse item : VodConfig.get().getParses()) {
            if (item.getType() == 1) {
                jxs.put(item.getName(), item.extUrl());
            }
        }
        JSONObject object = VodConfig.get().jsonExt(parse.getUrl(), jxs, webUrl);
        checkResult(MediaPlay.fromObject(object));
    }

    private void jsonMix(String webUrl, String flag) throws Throwable {
        LinkedHashMap<String, HashMap<String, String>> jxs = new LinkedHashMap<>();
        for (Parse item : VodConfig.get().getParses()) {
            jxs.put(item.getName(), item.mixMap());
        }
        JSONObject object = VodConfig.get().jsonExtMix(flag, parse.getUrl(), parse.getName(), jxs, webUrl);
        checkResult(MediaPlay.fromObject(object));
    }

    private void jsonParse(CountDownLatch latch, Parse item, String webUrl) {
        try {
            jsonParse(item, webUrl, false);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            latch.countDown();
        }
    }

    private void godParse(String webUrl, String flag) throws Exception {
        List<Parse> json = VodConfig.get().getParses(1, flag);
        List<Parse> webs = VodConfig.get().getParses(0, flag);
        CountDownLatch latch = new CountDownLatch(json.size());
        for (Parse item : json) infinite.execute(() -> jsonParse(latch, item, webUrl));
        latch.await();//等待所有线程执行完毕
        if (webs.isEmpty()) onParseError();
        for (Parse item : webs) startWeb(item, webUrl);
    }

    private void startWeb(Parse item, String webUrl) {
        startWeb("", item, webUrl);
    }

    private void startWeb(String key, Parse item, String webUrl) {
        startWeb(key, item.getName(), item.getHeaders(), item.getUrl() + webUrl, item.getClick());
    }

    private void startWeb(Map<String, String> headers, String url) {
        startWeb("", "", headers, url, "");
    }

    /***
     * 通过打开网页去解析播放地址
     * @param key
     * @param from
     * @param headers
     * @param url
     * @param click
     */

    private void startWeb(String key, String from, Map<String, String> headers, String url, String click) {
        CountDownLatch latch = new CountDownLatch(1);

        try {
            App.post(() -> {
                //报错 WebView 需要运行在主线程(UI线程)
                webViews.add(CustomWebView.create(App.getInstance()).start(key, from, headers, url, click, getCallback(latch), !url.contains("player/?url=")));
            });
            latch.wait(15 * 1000);//15秒超时
        } catch (InterruptedException e) {
            onParseError();
            throw new RuntimeException(e);
        }
        //
        Logger.t(TAG).d("从web获取解析播放结束");
    }

    private Map<String, String> mHeaders = null;
    private String mUrl = null;
    private String mFrom = null;

    private MediaPlay checkParseMediaPlay() {
        return checkResult(mHeaders, mUrl, mFrom, true);
    }

    private ParseCallback getCallback(CountDownLatch latch) {
        //锁
        return new ParseCallback() {
            @Override
            public void onParseSuccess(Map<String, String> headers, String url, String from) {
                mHeaders = headers;
                mUrl = url;
                mFrom = from;
                if (latch != null) {
                    latch.countDown();
                }

                Logger.t(TAG).d("onParseSuccess:" + " ,url=" + url + " ,headers=" + headers + " ,from=" + from);
            }

            @Override
            public void onParseError() {
                if (latch != null) {
                    latch.countDown();
                }
                Logger.t(TAG).d("onParseError:");
            }
        };
    }

    public interface ParseCallback {

        void onParseSuccess(Map<String, String> headers, String url, String from);

        void onParseError();
    }
}
